/**
 * 
 */
package com.googlecode.easyibatis.core.utils;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.LogFactory;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.googlecode.easyibatis.core.constants.CommonsConstants;
import com.googlecode.easyibatis.core.exception.EasyIbatisException;
import com.googlecode.easyibatis.core.model.Model;
import com.googlecode.easyibatis.core.model.annotation.Entity;
import com.googlecode.easyibatis.core.model.annotation.ManyToOne;
import com.googlecode.easyibatis.core.model.annotation.OneToMany;
import com.googlecode.easyibatis.core.model.annotation.SelectIgnore;
import com.googlecode.easyibatis.core.model.annotation.SysDate;

/**
 * @author zxq
 * 
 */
public final class ModelLoader {
	private final static org.apache.commons.logging.Log logger = LogFactory.getLog(ModelLoader.class);

	public final static String TABLE_NAME = "tableName";
	public final static String SEQUENCE_NAME = "sequenceName";
	public final static String FIELD_NAME_LISTS = "fieldNameLists";
	public final static String FIELD_NAMES = "fieldNames";
	public final static String VALUES = "values";
	public final static String KEY_VALUE_LISTS = "keyValueLists";

	@SuppressWarnings("unchecked")
	public static Map<Class, Map> modelCaches = Maps.newHashMap();

	/**
	 * 
	 */
	private ModelLoader() {
		super();
		if (logger.isErrorEnabled()) {
			logger.error("不能实例化此类");
		}
		throw new Error("不能实例化此类");
	}

	/**
	 * 
	 * @return
	 * @throws EasyIbatisException
	 */
	@SuppressWarnings("unchecked")
	public static Map loadWriteModel(Object object) throws EasyIbatisException {
		// 读取model缓存
		if (object != null && modelCaches.containsKey(object.getClass())) {
			return modelCaches.get(object.getClass());
		}
		try {
			// 模型属性
			String tableName = StringUtils.EMPTY;
			String sequenceName = StringUtils.EMPTY;
			// 获取实体上的Entity注释
			Entity entity = object.getClass().getAnnotation(Entity.class);
			if (entity != null && StringUtils.isNotBlank(entity.tableName())) {
				if (StringUtils.isNotBlank(entity.tableName())) {
					tableName = String.format("%s.%s", StringUtils.defaultIfEmpty(entity.schema(),
							CommonsConstants.DB_SCHEMA), entity.tableName());
				} else {
					if (logger.isErrorEnabled()) {
						logger.error("无法获取Entity.tableName");
					}
					throw new EasyIbatisException("无法获取Entity.tableName");
				}
				if (StringUtils.isNotBlank(entity.sequenceName())) {
					sequenceName = String.format("%s.%s", StringUtils.defaultIfEmpty(entity.schema(),
							CommonsConstants.DB_SCHEMA), entity.sequenceName());
				} else {
					if (logger.isErrorEnabled()) {
						logger.error("无法获取Entity.sequenceName");
					}
					throw new EasyIbatisException("无法获取Entity.sequenceName");
				}
			} else {
				if (logger.isErrorEnabled()) {
					logger.error("无法获取Entity");
				}
				throw new EasyIbatisException("无法获取Entity");
			}
			// 读取实体中常规属性的设置
			Field[] fields = object.getClass().getDeclaredFields();
			if (ArrayUtils.isEmpty(fields)) {
				if (logger.isErrorEnabled()) {
					logger.error("无法获取属性");
				}
				throw new EasyIbatisException("无法获取属性");
			}
			List<ModelHandler> modelHandlers = Lists.newArrayList();
			List<ManyToOneHandler> manyToOneHandlers = Lists.newArrayList();
			for (Field field : fields) {
				SelectIgnore selectIgnore = field.getAnnotation(SelectIgnore.class);
				ManyToOne manyToOne = field.getAnnotation(ManyToOne.class);
				SysDate sysDate = field.getAnnotation(SysDate.class);
				// 如果带有@SelectIgnore注解,则不在检索语句中加入此字段
				if (selectIgnore != null && selectIgnore.value()) {
					continue;
				}
				// 如果带有@SysDate注解,则该字段使用sysdate添加日期
				else if (sysDate != null) {
					modelHandlers.add(new ModelHandler(tableName, field.getName(), "sysdate"));
				}
				// 判断带有@ManyToOne注解的字段
				else if (manyToOne != null) {
					Object model = PropertyUtils.getNestedProperty(object, field.getName());
					if (model != null && model instanceof Model) {
						manyToOneHandlers.add(new ManyToOneHandler(StringUtils.defaultIfEmpty(manyToOne.columnName(),
								StringUtils.EMPTY), ((Model) model).getId()));
					}
					continue;
				}
				// 判断常规字段
				else {
					modelHandlers.add(new ModelHandler(tableName, field.getName(), PropertyUtils.getNestedProperty(
							object, field.getName())));
				}
			}
			Map map = Maps.newHashMap();
			map.put("tableName", tableName);
			map.put("sequenceName", sequenceName);
			map.put("id", (object instanceof Model ? ((Model) object).getId() : null));
			map.put("modelHandlers", modelHandlers);
			map.put("manyToOneHandlers", manyToOneHandlers);
			// model读取结果缓存
			modelCaches.put(object.getClass(), map);
			return map;
		} catch (Exception e) {
			if (logger.isErrorEnabled()) {
				logger.error("获取Model失败", e);
			}
			throw new EasyIbatisException("获取Model失败");
		}
	}

	/**
	 * 
	 * @param clazz
	 * @return
	 * @throws EasyIbatisException
	 */
	@SuppressWarnings("unchecked")
	public static Map loadSelectModel(Class<?> clazz) throws EasyIbatisException {
		String tableName = StringUtils.EMPTY;
		String sequenceName = StringUtils.EMPTY;
		// 读取实体类中@Entity注解配置的数据
		Entity entity = clazz.getAnnotation(Entity.class);
		if (entity != null && StringUtils.isNotBlank(entity.tableName())) {
			// 获取tableName
			if (StringUtils.isNotBlank(entity.tableName())) {
				tableName = String.format("%s.%s", StringUtils.defaultIfEmpty(entity.schema(),
						CommonsConstants.DB_SCHEMA), entity.tableName());
			} else {
				if (logger.isErrorEnabled()) {
					logger.error("无法获取Entity.tableName");
				}
				throw new EasyIbatisException("无法获取Entity.tableName");
			}
			// 获取sequenceName
			if (StringUtils.isNotBlank(entity.sequenceName())) {
				sequenceName = String.format("%s.%s", StringUtils.defaultIfEmpty(entity.schema(),
						CommonsConstants.DB_SCHEMA), entity.sequenceName());
			} else {
				if (logger.isErrorEnabled()) {
					logger.error("无法获取Entity.sequenceName");
				}
				throw new EasyIbatisException("无法获取Entity.sequenceName");
			}
		} else {
			if (logger.isErrorEnabled()) {
				logger.error("无法获取Entity");
			}
			throw new EasyIbatisException("无法获取Entity");
		}
		// 读取实体类中常规属性的设置
		Field[] fields = clazz.getDeclaredFields();
		if (ArrayUtils.isEmpty(fields)) {
			if (logger.isErrorEnabled()) {
				logger.error("无法获取属性");
			}
			throw new EasyIbatisException("无法获取属性");
		}
		List<ModelHandler> modelHandlers = Lists.newArrayList();
		// 添加id字段
		modelHandlers.add(new ModelHandler(tableName, "id"));

		List<ManyToOneHandler> manyToOneHandlers = Lists.newArrayList();

		int manyToOneHandlerAliasTableNameNumber = 1;
		for (Field field : fields) {
			SelectIgnore selectIgnore = field.getAnnotation(SelectIgnore.class);
			ManyToOne manyToOne = field.getAnnotation(ManyToOne.class);
			OneToMany oneToMany = field.getAnnotation(OneToMany.class);

			// 如果带有@SelectIgnore注解,则不在检索语句中加入此字段
			if (selectIgnore != null && selectIgnore.value()) {
				continue;
			}
			// 判断带有@ManyToOne注解的字段
			else if (manyToOne != null) {
				List<StringHandler> selectReferencedColumnNames = Lists.newArrayList();
				for (String s : manyToOne.selectReferencedColumnNames()) {
					selectReferencedColumnNames.add(new StringHandler(s));
				}
				manyToOneHandlers.add(new ManyToOneHandler(field.getName(), manyToOne.columnName(), String.format(
						"%s.%s", StringUtils.defaultIfEmpty(entity.schema(), CommonsConstants.DB_SCHEMA), manyToOne
								.referencedTableName()), String.format("t%d", manyToOneHandlerAliasTableNameNumber),
						manyToOne.referencedColumnName(), selectReferencedColumnNames));
				manyToOneHandlerAliasTableNameNumber++;
				continue;
			} // TODO 判断带有@OneToMany注解的字段
			else if (oneToMany != null) {
				continue;
			}
			// 判断常规字段
			else {
				modelHandlers.add(new ModelHandler(tableName, field.getName()));
			}
		}
		Map map = Maps.newHashMap();
		map.put("tableName", tableName);
		map.put("modelHandlers", modelHandlers);
		map.put("manyToOneHandlers", manyToOneHandlers);
		return map;
	}

}
